package state

import (
	"fmt"
	"luu/binchunk"
)

/**
反编译
*/
func PrintProto(path string) {
	list(binchunk.GetProtoFromFile(path))
}

func list(proto *binchunk.Prototype) {
	printHeader(proto)
	printCode(proto)
	printDetail(proto)
	for _, prototype := range proto.Protos {
		list(prototype)
	}
}

func printHeader(f *binchunk.Prototype) {
	funcType := "main"
	if f.LineDefined > 0 {
		funcType = "function"
	}
	varArgFlag := ""
	if f.IsVarArg > 0 {
		varArgFlag = "+"
	}
	fmt.Printf("%s<%s:(%d->%d)>(%d codes/instructions)\n", funcType, f.Source, f.LineDefined, f.LastLineDefined, len(f.Code))
	fmt.Printf("%d%s params, %d slots , %d upvalues, %d locals, %d consts ,%d functions\n", f.NumParams, varArgFlag,
		f.MaxStackSize, len(f.UpValues), len(f.LocVars), len(f.Constants), len(f.Protos))
}

func printCode(f *binchunk.Prototype) {
	fmt.Printf("Codes(%d)\n", len(f.Code))
	for i, u := range f.Code {
		ins := Instruction(u)
		printInstruction(ins, f, nil, i)
	}
}

func printInstruction(i Instruction, f *binchunk.Prototype, vm LuaVM, line int) {
	fmt.Printf("[%d] \t %s  ", line, i.OpName())
	switch i.OpMode() {
	case IABC:
		a, b, c := i.ABC()
		fmt.Printf("%d", a)
		if i.BMode() == OpArgU {
			fmt.Printf(" $%d", b)
		} else if i.BMode() != OpArgN {
			PrintABCOperands(b, f, vm)
		}
		if i.CMode() == OpArgU {
			fmt.Printf(" $%d", c)
		} else if i.CMode() != OpArgN {
			PrintABCOperands(c, f, vm)
		}
		break
	case IABx:
		a, bx := i.ABx()
		fmt.Printf("%d", a)
		if i.BMode() == OpArgK {
			ci := bx & 0xFF
			cv := f.Constants[ci]
			fmt.Printf(" c[%d](%s)", ci, constToString(cv))
		} else if i.BMode() == OpArgU {
			fmt.Printf(" $%d", bx)
		}
		break
	case IAsBx:
		a, sbx := i.AsBx()
		fmt.Printf("%d {%d}", a, sbx)
		break
	case IAx:
		fmt.Printf("{%d}", -1-i.Ax())
		break
	default:
		panic("error opMode")
	}
	fmt.Println()
}

// ABC 中的BC参数
func PrintABCOperands(b int, proto *binchunk.Prototype, vm LuaVM) {
	if b > 0xFF { //最高位是1，代表常量表索引 todo-xxd 这里的含义是什么 ？？？
		ci := b & 0xFF
		cv := proto.Constants[ci]
		fmt.Printf(" c[%d](%s)", ci, constToString(cv))
	} else {
		regV := "*"
		if vm != nil {
			regV = vm.PeekStack(b + 1)
		}
		fmt.Printf(" s[%d](%s)", b+1, regV)
	}
}

func printDetail(f *binchunk.Prototype) {
	PrintConst(f)
	fmt.Printf("locVars:(%d)\n", len(f.LocVars))
	fmt.Println("\t  varName \t startPC \t endPC\n")
	for i, locVar := range f.LocVars {
		fmt.Printf("\t %d \t %s \t %d \t  %d\n", i, locVar.Varname, locVar.StartPC, locVar.EndPC)
	}

	fmt.Printf("upvalues:(%d)\n", len(f.UpValues))
	fmt.Printf("\t   \tupvalName\tInstack\tIdx \n")
	for i, value := range f.UpValues {
		upvalName := "-"
		if len(f.UpvalueNames) > 0 {
			upvalName = f.UpvalueNames[i]
		}
		fmt.Printf("\t %d \t %s \t\t %d \t\t %d \n", i, upvalName, value.Instack, value.Idx)
	}
}

func PrintConst(f *binchunk.Prototype) {
	fmt.Printf("const: (%d)\n", len(f.Constants))
	for i, constant := range f.Constants {
		fmt.Printf("\t %d \t %s\n", i, constToString(constant))
	}
}

func constToString(k interface{}) string {
	switch k.(type) {
	case nil:
		return "nil"
	case bool:
		return fmt.Sprintf("%t", k)
	case float64:
		return fmt.Sprintf("%g", k)
	case int64:
		return fmt.Sprintf("%d", k)
	case string:
		return fmt.Sprintf("%q", k)
	default:
		return "?"
	}
}
